package com.hlkj.pay.app.merchant.ext.impl.heepay.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * RSA加解密工具类
 *
 * @Date: 2021/10/14 20:39
 * @Author: pandans
 */
public class HeePaySignUtil {

	public static final String ALGORITHM = "RSA";

	public static final String BC_PROV_ALGORITHM_SHA1RSA = "SHA1withRSA";

	private static final int MAX_ENCRYPT_BLOCK = 117;

	private static final int MAX_DECRYPT_BLOCK = 256;

	public static boolean verify(String data, String sign, String publicKeyStr) {
		try {
			PublicKey publicKey = getPublicKey(publicKeyStr);
			Signature signature = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA);
			signature.initVerify(publicKey);
			signature.update(data.getBytes(StandardCharsets.UTF_8));
			return signature.verify(org.bouncycastle.util.encoders.Base64.decode(sign.getBytes()));
		} catch (Exception e) {
			return false;
		}
	}

	public static String sign(String obj, String privateKeyStr) {
		String sign = "";
		try {
			SortedMap<String,Object> reqMap = JSONObject.parseObject(obj, new TypeReference<TreeMap<String,Object>>(){});
			String signContent = createSignStr(reqMap);
			System.out.println("签名串：--------->" + signContent);
			PrivateKey privateKey = getPrivateKey(privateKeyStr);
			Signature signature = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA);
			signature.initSign(privateKey);
			signature.update(signContent.getBytes(StandardCharsets.UTF_8));
			sign = new String(org.bouncycastle.util.encoders.Base64.encode(signature.sign()));
		} catch (Exception e) {
		}
		return sign;
	}

	/**
	 * 加密优化
	 * 1.避免重复转换字符串到字节数组：
	 * 在原始代码中，data.getBytes() 被多次调用，这会导致不必要的开销。应该将字节数组存储在一个变量中，然后重用这个变量。
	 * 2.使用更高效的字节数组处理方式：
	 * 使用 ByteArrayOutputStream 和 doFinal 方法处理数据块时，可以考虑直接使用一个固定大小的缓冲区来减少内存分配和复制的开销。
	 * 3.正确计算和处理数据块：
	 * 原始代码在处理最后一个数据块时可能存在问题，因为 offset 的计算方式可能导致数组越界或数据遗漏。
	 * 4.使用更高效的Base64编码库：
	 * 虽然 org.bouncycastle.util.encoders.Base64 是一个强大的库，但Java标准库中的 java.util.Base64 通常更高效且更易于集成。
	 */
	public static String encrypt(String signContent, String publicKeyStr) throws Exception {
//		SortedMap<String,Object> reqMap = JSONObject.parseObject(data, new TypeReference<TreeMap<String,Object>>(){});
//		String signContent = JsonUtils.toJsonString(reqMap);
		System.out.println("加密串：--------->" + signContent);
		PublicKey publicKey = getPublicKey(publicKeyStr);
		Cipher cipher = Cipher.getInstance(ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);

		// 将字符串转换为字节数组一次
		byte[] dataBytes = signContent.getBytes(StandardCharsets.UTF_8);
		int inputLen = dataBytes.length;

		// 使用ByteArrayOutputStream收集加密后的数据
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offset = 0;

		while (inputLen - offset > 0) {
			int blockSize = Math.min(MAX_ENCRYPT_BLOCK, inputLen - offset);
			byte[] encryptedBlock = cipher.doFinal(dataBytes, offset, blockSize);
			out.write(encryptedBlock);
			offset += blockSize;
		}

		byte[] encryptedData = out.toByteArray();
		out.close();

		// 使用Java标准库的Base64编码
		return Base64.getEncoder().encodeToString(encryptedData);
	}

	public static String decrypt(String data, String privateKeyStr) throws Exception {
		PrivateKey privateKey = getPrivateKey(privateKeyStr);
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		byte[] dataBytes = org.bouncycastle.util.encoders.Base64.decode(data.getBytes());
		int inputLen = dataBytes.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offset = 0;
		byte[] cache;
		int i = 0;
		while (inputLen - offset > 0) {
			if (inputLen - offset > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
			}
			out.write(cache, 0, cache.length);
			i++;
			offset = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return new String(decryptedData, StandardCharsets.UTF_8);
	}

	private static RSAPublicKey getPublicKey(String publicKey) throws Exception {
		byte[] keyBytes = Base64.getDecoder().decode(publicKey);
		X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
		return (RSAPublicKey) keyFactory.generatePublic(spec);
	}

	private static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
		byte[] keyBytes = Base64.getDecoder().decode(privateKey);
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
		return (RSAPrivateKey) keyFactory.generatePrivate(spec);
	}

	/**
	 * @param parameters 数据对象 不能都为空
	 * @return 签名排序字符串
	 */
	public static String createSignStr(SortedMap<String, Object> parameters) {
		StringBuilder sb = new StringBuilder();
		Set<Map.Entry<String,Object>> es = parameters.entrySet();
		for (Map.Entry<String,Object> e : es) {
			String k =  e.getKey();
			String v = (e.getValue() == null ||
					e.getValue() instanceof List ||
					e.getValue().getClass().isArray()
			) ? "" : e.getValue().toString();
			//空值不传递，不参与签名组串
			if (!"signature".equals(k) && !"sign".equals(k)) {
				sb.append(k).append("=").append(v).append("&");
			}
		}
		return sb.substring(0,sb.length() -1);
	}
	//&字符串转map
	public static Map<String, String> getURLParameters(String query) {
		Map<String, String> parameters = new HashMap<>();

		try {
			String[] pairs = query.split("&");

			for (String pair : pairs) {
				int idx = pair.indexOf("=");
				String key = URLDecoder.decode(pair.substring(0, idx), "UTF-8");
				String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8");

				// 将空格替换为加号
				value = value.replace(" ", "+");

				parameters.put(key, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

        return parameters;
	}

	public static void main(String[] args) {
		String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7fkqdF4puVotzhIvITw+qm/hHE4XUbUm0zAemA1T8y1nxhJuHg3aJh/WhRTp5gDgaibl7vLkLd422CRIxCCCj20fGdVbdnHImYqeh3MlSiu7NIn5gfRg1oWQZUeuQWRgH9SJzKLYH5Hi4oqynjNSa+AMKi3ug188TFSRxAQUwLFcImFQe84wIBrb2iXmQpq2VHHxF0gf9tYdMJ3Qbe/tWZPa2zF/+045WG6yQfwXBYOPCfDxLtHMtZUzPTrA9LBueFEY+TEKE/NRbUuy11br++HxJQKAI9g3pX/GvLBWvk983LGEZ6MU1rJBhvbfyuP98qbUP5EBd90GzR/WLFlI7wIDAQAB";
		String privateKeyStr = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4z13yWTHJ2XLZW/S7W9fs/4sz5MBn32f6VpH+INVY/UKqgfoCt2lzv3ta7JjNYjjLp+PhFVres4xoezJ2VT4sh9B2mrcViCrU4jBwDdqdPy/l+it4jQHXqtIxbanzP02aJIFfJf6OZeNt8c6LU1az+ZnXGW3LLpW3tnCanQBCT1+E3ajftfmMuWL57WOGqF/hyd5z7UzOWMnmPIW+rPCyOMsllq3EKO9CIZ0tsTTkJ1JtBLDPsv2zIGBC82hTpAyb1ks/L/3byJq8C3FDgvrMVtxU8L7jVvdi/Cyq8GgX6dwe4KQp3/gsKIEr+huib80nTwlP7vZPIXTV0VEZcl81AgMBAAECggEAPPM2+qxr/TQX6pOnPPtnVSRaVn0E770AX9GZSISvmC++gtlnCOpQfvoRxsTtCB9yt8fhIeSXGYeBsRugYuJuJOWN3O/dnd5QsfD/JJWNGfsy4tgE6jcm+me99stCF5UzFLkhaa3wLCpyPVpYUh7kI2sYT5Z1XWM5wWML9BH/ed5/rO4bmF79gmITIMs2B2X8Vq6o9uAJWxhFoJ5lAjfoUruhiPeVgpMdlZ4Z7SZVHbnGOks6GublXAckGFBq7Jm44HWS0RiTb4TXVigu/QoVYSaQy72B6mW5pNQ4CBYNwdVEEcStVaRi+6JAVLshbsdvUtsMRYkewRglHWxJfcUc6QKBgQDrCRo+ClscHqy8l1qTkuP0uhFu37smkBE9jywiCfWk8ZeY+2B33cUWQh21V6jC7PBFuRD1haMKPhVXdukdw1SGJAZ5zV9PiIUlPZ5IM2pLuFSjKw9nc3GNnMiak+GHFQcB+1DTYKp7LDrTwkcti/Lq1tB4U5tRLbBew32Fby3EWwKBgQDJS2TxJARbRRLQkE/JyTkKGJhv5HCvE9WZQ20WHotxPlgjTiI8UU/UQPLhC9+ggznwrcafzy3RpJp3QyOfhZJ5iDLkMqVBAJyQfJurtpziNOmWihlGGS1mzn52iXK6KemUrCPUvPciFWYuMd8znT2Wadw3e/FuAVtorZBtSkB/rwKBgQCoYlUSrYiolLFcS3Lw1wPJiKSEsnT6I6y2VaRgnpIGIa9iQnXSameQkr2F9c9KF0XsLbIgtCUiaWgQyXgpgo9DqvuYt7HY4Q3CqGqFf5Bfvyej3m2ix+pKclClrdydUks6+DnnthFtPTrICx/DWcgmxs+D5SIY/oqk2ZlLx2MakQKBgGI4IZbwRhfqylVuNSB2yRg+DBN82ebzvKO+HLpQSSYGj6caduYLnvIohtp8mq+NmHXxtRtBUlAbCyvqXLV3zZLzfDGQ7MWc8MgnpSXSQxa1Ts0fsVlc66RgWaCpTIu967WZkRo64jOFUYh02T4sGWMNYGDKX3GXw+x91B0fv/0NAoGBAKw3N2JV6S72Z6eDLbJ6y/WVKX8fJ4wMEokVcHgwCcy1J2l7kmPoPHqD4Qif5k2r4mpYv3MRKtiPWpj4B5R2Jhh+ub73oL1khRgDK9HzYgQreDEOrjORmC6iL/XT8qAlfHoZeDf/l5+CNVYCLKkugo+WRIrphWXoU6luJ1zOGeic";

		try {
			Map<String, String> map = new HashMap<>();
			map.put("agent_id", "2332946");
			map.put("encrypt_data", "lRDbCywy8OaShFy2vQpzuiLk2VCtr56KNSxouF5MPvFsIEMfEuhDIm9nepOaVD3D/f3h/ULM1qFstZI6sQ4FBNMgZ7MQjy57Ap1MJL7Dl+2eNEZSCASWlIXQrbcNtQKqd5eJn6XQ8nSKIuksuSg0LGQFCA7lFdIp7ou0+bwLMnR3OKlZF2YiB9HhzWe9Fwff0hAjexQUkafLVLnl1DUmiZXfVfAm30skkyCEUDLvX2x0Y293+qr4SC0jnix2xKnL+08MM2iipIUD5pvLJlaksuOcwYG5Hhb6wBR1meqNI5+PtIMltR01uAh6AGUuY9/49T615Idqal3xGgaFBeUU0XlP9LLNd4Vdcg16lMKqkac8QdiGMfbYFRGkXqu82tszpgtZH2Ts8mVo26CwvUIc3wuIEY3Cxp/NASvwoDETeHrJKPQzSf+1/mtI0cUQb0F1S0AU5UyHXMqk79IJL2LZHWG8XZ7p8G1yE2OIcPWyqrbNWRb4e84kgRGYMcnQfo1BwTppRv3I6X935gTPTA4KFwMWsPV1JmCezgaWeZCKjMi8EP2KCaVg9iMqcPso+yJMDdmPNBrQVzWFFO2C8gH9DWUxe1tq77miJrv93gySQegeEL3wGUnCbVB8zB5lhUtHsDgYJAhN6h/LFe01jo0QGpsZwCLEtRmaNRZbPXp3xus=");
			map.put("sign", "a8syp0K4R6/myCS1JLN3DYjotiH0R1RcVc9tSW0PmIMhRBrqczlGbNYMSmF1JUxQTTYPSDsE0d2ILBRPms1ICRuASmG/9SLbsCTG32ViMJYSSo7/4ZpvmAvoaif/yczpRzUN/OaXfYeYiL0JQmCUqXK3a+tnL7phVWrMF+nbY0ywAB/Qz2YYmE6Bym3BteA4OKxoraXORbSnQy2jJfUxzmR7qquar6Ql3sgJwUlVPR+H2f/XTFTvEtDYZKOBaun/JKS+dnz8cf4f8VjvI2ekX0ZZ0RxlTFGKdKtxw3KitCjSvh/tzv8mwop04h0igTP6bPZxcw/884op/tzG7c58bQ==");

			String encryptText = map.get("encrypt_data");
			System.out.println(encryptText);
			String decryptText = decrypt(encryptText, privateKeyStr);
			System.out.println("解密后内容：" + decryptText);

			boolean flga = verify(decryptText, map.get("sign"), publicKeyStr);
			System.out.println(flga);
			System.out.println(getURLParameters(decryptText));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
